/* -------------------------------------------------------------------------------

Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.

This file is part of GtkRadiant.

GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

----------------------------------------------------------------------------------

Foliage code for Wolfenstein: Enemy Territory by ydnar@splashdamage.com

------------------------------------------------------------------------------- */



/* marker */
#define SURFACE_FOLIAGE_C



/* dependencies */
#include "q3map2.h"



#define MAX_FOLIAGE_INSTANCES	8192

static int      numFoliageInstances;
static foliageInstance_t foliageInstances[MAX_FOLIAGE_INSTANCES];



/*
SubdivideFoliageTriangle_r()
recursively subdivides a triangle until the triangle is smaller than
the desired density, then pseudo-randomly sets a point
*/

static void SubdivideFoliageTriangle_r(mapDrawSurface_t * ds, foliage_t * foliage, bspDrawVert_t ** tri)
{
	bspDrawVert_t   mid, *tri2[3];
	int             max;


	/* limit test */
	if(numFoliageInstances >= MAX_FOLIAGE_INSTANCES)
		return;

	/* plane test */
	{
		vec4_t          plane;


		/* make a plane */
		if(!PlaneFromPoints(plane, tri[0]->xyz, tri[1]->xyz, tri[2]->xyz, qtrue))
			return;

		/* if normal is too far off vertical, then don't place an instance */
		if(plane[2] < 0.5f)
			return;
	}

	/* subdivide calc */
	{
		int             i;
		float          *a, *b, dx, dy, dz, dist, maxDist;
		foliageInstance_t *fi;


		/* get instance */
		fi = &foliageInstances[numFoliageInstances];

		/* find the longest edge and split it */
		max = -1;
		maxDist = 0.0f;
		VectorClear(fi->xyz);
		VectorClear(fi->normal);
		for(i = 0; i < 3; i++)
		{
			/* get verts */
			a = tri[i]->xyz;
			b = tri[(i + 1) % 3]->xyz;

			/* get dists */
			dx = a[0] - b[0];
			dy = a[1] - b[1];
			dz = a[2] - b[2];
			dist = (dx * dx) + (dy * dy) + (dz * dz);

			/* longer? */
			if(dist > maxDist)
			{
				maxDist = dist;
				max = i;
			}

			/* add to centroid */
			VectorAdd(fi->xyz, tri[i]->xyz, fi->xyz);
			VectorAdd(fi->normal, tri[i]->normal, fi->normal);
		}

		/* is the triangle small enough? */
		if(maxDist <= (foliage->density * foliage->density))
		{
			float           alpha, odds, r;


			/* get average alpha */
			if(foliage->inverseAlpha == 2)
				alpha = 1.0f;
			else
			{
				alpha = ((float)tri[0]->lightColor[0][3] + (float)tri[1]->lightColor[0][3] + (float)tri[2]->lightColor[0][3]) / 765.0f;
				if(foliage->inverseAlpha == 1)
					alpha = 1.0f - alpha;
				if(alpha < 0.75f)
					return;
			}

			/* roll the dice */
			odds = foliage->odds * alpha;
			r = Random();
			if(r > odds)
				return;

			/* scale centroid */
			VectorScale(fi->xyz, 0.33333333f, fi->xyz);
			if(VectorNormalize(fi->normal) == 0.0f)
				return;

			/* add to count and return */
			numFoliageInstances++;
			return;
		}
	}

	/* split the longest edge and map it */
	LerpDrawVert(tri[max], tri[(max + 1) % 3], &mid);

	/* recurse to first triangle */
	VectorCopy(tri, tri2);
	tri2[max] = &mid;
	SubdivideFoliageTriangle_r(ds, foliage, tri2);

	/* recurse to second triangle */
	VectorCopy(tri, tri2);
	tri2[(max + 1) % 3] = &mid;
	SubdivideFoliageTriangle_r(ds, foliage, tri2);
}



/*
GenFoliage()
generates a foliage file for a bsp
*/

void Foliage(mapDrawSurface_t * src)
{
	int             i, j, k, x, y, pw[5], r, oldNumMapDrawSurfs;
	mapDrawSurface_t *ds;
	shaderInfo_t   *si;
	foliage_t      *foliage;
	mesh_t          srcMesh, *subdivided, *mesh;
	bspDrawVert_t  *verts, *dv[3], *fi;
	vec3_t          scale;
	matrix_t        transform, rotation;


	/* get shader */
	si = src->shaderInfo;
	if(si == NULL || si->foliage == NULL)
		return;

	/* do every foliage */
	for(foliage = si->foliage; foliage != NULL; foliage = foliage->next)
	{
		/* zero out */
		numFoliageInstances = 0;

		/* map the surface onto the lightmap origin/cluster/normal buffers */
		switch (src->type)
		{
			case SURFACE_META:
			case SURFACE_FORCED_META:
			case SURFACE_TRIANGLES:
				/* get verts */
				verts = src->verts;

				/* map the triangles */
				for(i = 0; i < src->numIndexes; i += 3)
				{
					dv[0] = &verts[src->indexes[i]];
					dv[1] = &verts[src->indexes[i + 1]];
					dv[2] = &verts[src->indexes[i + 2]];
					SubdivideFoliageTriangle_r(src, foliage, dv);
				}
				break;

			case SURFACE_PATCH:
				/* make a mesh from the drawsurf */
				srcMesh.width = src->patchWidth;
				srcMesh.height = src->patchHeight;
				srcMesh.verts = src->verts;
				subdivided = SubdivideMesh(srcMesh, 8, 512);

				/* fit it to the curve and remove colinear verts on rows/columns */
				PutMeshOnCurve(*subdivided);
				mesh = RemoveLinearMeshColumnsRows(subdivided);
				FreeMesh(subdivided);

				/* get verts */
				verts = mesh->verts;

				/* map the mesh quads */
				for(y = 0; y < (mesh->height - 1); y++)
				{
					for(x = 0; x < (mesh->width - 1); x++)
					{
						/* set indexes */
						pw[0] = x + (y * mesh->width);
						pw[1] = x + ((y + 1) * mesh->width);
						pw[2] = x + 1 + ((y + 1) * mesh->width);
						pw[3] = x + 1 + (y * mesh->width);
						pw[4] = x + (y * mesh->width);	/* same as pw[ 0 ] */

						/* set radix */
						r = (x + y) & 1;

						/* get drawverts and map first triangle */
						dv[0] = &verts[pw[r + 0]];
						dv[1] = &verts[pw[r + 1]];
						dv[2] = &verts[pw[r + 2]];
						SubdivideFoliageTriangle_r(src, foliage, dv);

						/* get drawverts and map second triangle */
						dv[0] = &verts[pw[r + 0]];
						dv[1] = &verts[pw[r + 2]];
						dv[2] = &verts[pw[r + 3]];
						SubdivideFoliageTriangle_r(src, foliage, dv);
					}
				}

				/* free the mesh */
				FreeMesh(mesh);
				break;

			default:
				break;
		}

		/* any origins? */
		if(numFoliageInstances < 1)
			continue;

		/* remember surface count */
		oldNumMapDrawSurfs = numMapDrawSurfs;

		/* set transform matrix */
		VectorSet(scale, foliage->scale, foliage->scale, foliage->scale);
		MatrixSetupScale(transform, scale[0], scale[1], scale[2]);
		MatrixIdentity(rotation);

		/* add the model to the bsp */
		InsertModel(foliage->model, 0, transform, rotation, NULL, NULL, src->entityNum, src->castShadows, src->recvShadows, 0,
					src->lightmapScale, 0, 0);

		/* walk each new surface */
		for(i = oldNumMapDrawSurfs; i < numMapDrawSurfs; i++)
		{
			/* get surface */
			ds = &mapDrawSurfs[i];

			/* set up */
			ds->type = SURFACE_FOLIAGE;
			ds->numFoliageInstances = numFoliageInstances;

			/* a wee hack */
			ds->patchWidth = ds->numFoliageInstances;
			ds->patchHeight = ds->numVerts;

			/* set fog to be same as source surface */
			ds->fogNum = src->fogNum;

			/* add a drawvert for every instance */
			verts = safe_malloc((ds->numVerts + ds->numFoliageInstances) * sizeof(*verts));
			memset(verts, 0, (ds->numVerts + ds->numFoliageInstances) * sizeof(*verts));
			memcpy(verts, ds->verts, ds->numVerts * sizeof(*verts));
			free(ds->verts);
			ds->verts = verts;

			/* copy the verts */
			for(j = 0; j < ds->numFoliageInstances; j++)
			{
				/* get vert (foliage instance) */
				fi = &ds->verts[ds->numVerts + j];

				/* copy xyz and normal */
				VectorCopy(foliageInstances[j].xyz, fi->xyz);
				VectorCopy(foliageInstances[j].normal, fi->normal);

				/* ydnar: set color */
				fi->paintColor[0] = 1.0f;
				fi->paintColor[1] = 1.0f;
				fi->paintColor[2] = 1.0f;
				fi->paintColor[3] = 1.0f;

				for(k = 0; k < MAX_LIGHTMAPS; k++)
				{
					fi->lightColor[k][0] = 255;
					fi->lightColor[k][1] = 255;
					fi->lightColor[k][2] = 255;
					fi->lightColor[k][3] = 255;
				}
			}

			/* increment */
			ds->numVerts += ds->numFoliageInstances;
		}
	}
}
